//=========================================================================== // VISUALIZING AND SONIFYING A SINE WAVE // N. Gessler 13 September 2015 //=========================================================================== #include #pragma hdrstop #include "Unit1.h" #include // enables MIDI //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; //=========================================================================== // VARIABLES //=========================================================================== float x, y; // for the MIDI device int midiport = 0; HMIDIOUT device; union { public: unsigned long word; unsigned char data[4]; } message; int instrument = 10; // "marimba" but sounds like 13 xylophone int note; int whole = 500; //=========================================================================== // FUNCTIONS //=========================================================================== //--------------------------------------------------- Color Ramp Version 2012 TColor colorRamp(int part, int whole) { if (whole == 0) whole = 1; // prevent divide by zero part = part % whole; // keep part <= whole int pixelDistanceAlongEdges = (part * 1792) / whole; int red, green, blue; // Which edge of the color cube are we on? if (pixelDistanceAlongEdges < 256) { // from BLACK to BLUE red = 0; green = 0; blue = pixelDistanceAlongEdges; } else if (pixelDistanceAlongEdges < 512) { // from BLUE to CYAN red = 0; green = pixelDistanceAlongEdges - 256; blue = 255; } else if (pixelDistanceAlongEdges < 768) { // from CYAN to GREEN red = 0; green = 255; blue = 255 - (pixelDistanceAlongEdges - 512); } else if (pixelDistanceAlongEdges < 1024) { // from GREEN to YELLOW red = (pixelDistanceAlongEdges - 768); green = 255; blue = 0; } else if (pixelDistanceAlongEdges < 1280) { // from YELLOW to RED red = 255; green= 255-(pixelDistanceAlongEdges - 1024); blue = 0; } else if (pixelDistanceAlongEdges < 1536) { // from RED to MAGENTA red = 255; green= 0; blue = pixelDistanceAlongEdges - 1280; } else { // from MAGENTA to WHITE red = 255; green = pixelDistanceAlongEdges - 1537; blue = 255; } return static_cast(RGB(red, green, blue)); } //------------------------------------------------------- 2012.01.21 midiRamp // Be aware that the range of notes is different for different instruments. // This code does not take that into account. int midiRamp(int part, int whole) { // only using the 47 MIDI notes which range from 35 to 81 if (whole == 0) whole = 1; // prevent divide by zero part = part % whole; // keep part less than whole note = (part * 47) / whole; note = note + 35; return note; } void drawBox (void) { Form1->PaintBox1->Canvas->MoveTo(0, 0); Form1->PaintBox1->Canvas->LineTo(Form1->PaintBox1->Width - 1, 0); Form1->PaintBox1->Canvas->LineTo(Form1->PaintBox1->Width - 1, Form1->PaintBox1->Height - 1); Form1->PaintBox1->Canvas->LineTo(0, Form1->PaintBox1->Height - 1); Form1->PaintBox1->Canvas->LineTo(0, 0); } // Creates a sine wave 500 pixels high beginning at 0 void sineWave (void) { Form1->PaintBox1->Refresh(); Form1->PaintBox1->Canvas->Pen->Width = 4; Form1->PaintBox1->Canvas->Pen->Color = clRed; Form1->PaintBox1->Canvas->Brush->Color = clGray; Form1->PaintBox1->Canvas->Rectangle(0, 0, 660, 500); //drawBox(); Form1->PaintBox1->Canvas->Pen->Width = 1; Form1->PaintBox1->Canvas->Pen->Color = clBlack; Form1->PaintBox1->Canvas->MoveTo(800, 500); Form1->PaintBox1->Canvas->LineTo(0, 500); for (x = M_PI / 2; x <= 2. * M_PI + M_PI / 2; x+=.05) { y = 250 + 250 * sin(x); Sleep(60); // interval between notes & colors // select the viusualization color and draw an ellipse Form1->PaintBox1->Canvas->Pen->Color = colorRamp(y, whole); Form1->PaintBox1->Canvas->Brush->Color = colorRamp(500 - y, whole); Form1->PaintBox1->Canvas->Ellipse(x * 100 - 150, y, x * 100 - 150 + 20, y + 20); // select a sonification note and play it //if (y == 0) continue; // if we want 0 to be silent note = midiRamp(500 - y, whole); // the call message.data[0] = 0x90; // note on message.data[1] = note; // note # midiOutShortMsg(device, message.word); // do it Form1->EditX->Text = (x * 100 - 150); Form1->EditY->Text = (500 - y); Application->ProcessMessages(); } } //=========================================================================== // EVENT HANDLERS //=========================================================================== //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { DoubleBuffered = true; // corrects display glitches // set up midi device midiOutOpen(&device, midiport, 0, 0, CALLBACK_NULL); message.data[0] = 0xC0; // choose instrument command message.data[1] = instrument; // instrument message.data[2] = 100; // once set, no need to repeat message.data[3] = 0; // once set, no need to repeat midiOutShortMsg(device, message.word); } //--------------------------------------------------------------------------- void __fastcall TForm1::ButtonRunClick(TObject *Sender) { sineWave(); } //--------------------------------------------------------------------------- void __fastcall TForm1::TrackBarWholeChange(TObject *Sender) { whole = TrackBarWhole->Position; Form1->EditWhole->Text = whole; } //--------------------------------------------------------------------------- void __fastcall TForm1::EditWholeChange(TObject *Sender) { Form1->TrackBarWhole->Position = StrToInt(EditWhole->Text); } //---------------------------------------------------------------------------